﻿using System;
using System.Text;
using System.Xml.Serialization;

namespace System.Collections.Generic {
	[XmlRoot("dictionary")]
	public class SerializableDictionary<TKey, TValue> : Dictionary<TKey, TValue>, IXmlSerializable {
		#region IXmlSerializable Members
		public System.Xml.Schema.XmlSchema GetSchema() {
			return null;
		}

		public void ReadXml( System.Xml.XmlReader reader ) {
			XmlSerializer keySerializer;
			XmlSerializer valueSerializer;

			bool wasEmpty = reader.IsEmptyElement;
			reader.Read();

			if( wasEmpty )
				return;
			
			if( !s_Serializers.TryGetValue(typeof(TKey),out keySerializer) )
				keySerializer = s_Serializers[typeof(TKey)] = new XmlSerializer( typeof(TKey) );

			while( reader.NodeType != System.Xml.XmlNodeType.EndElement ) {
				reader.ReadStartElement( "item" );

				reader.ReadStartElement( "key" );
				TKey key = (TKey)keySerializer.Deserialize( reader );
				reader.ReadEndElement();

				reader.ReadToFollowing( "value" );
					Type type;
					if( reader.HasAttributes ) {
						String attr = reader.GetAttribute( "type" );
						type = Type.GetType( attr );
					} else {
						type = typeof( TValue );
					}
				reader.ReadStartElement( "value" );
					if( !s_Serializers.TryGetValue(type,out valueSerializer) )
						valueSerializer = s_Serializers[type] = new XmlSerializer( type );
					TValue value = (TValue)valueSerializer.Deserialize( reader );
				reader.ReadEndElement();

				this.Add( key, value );

				reader.ReadEndElement();
				reader.MoveToContent();
			}
			reader.ReadEndElement();
		}

		public void WriteXml( System.Xml.XmlWriter writer ) {
			XmlSerializer keySerializer;
			XmlSerializer valueSerializer;

			if( !s_Serializers.TryGetValue(typeof(TKey),out keySerializer) )
				keySerializer = s_Serializers[typeof(TKey)] = new XmlSerializer( typeof(TKey) );

			foreach(TKey key in this.Keys) {
				writer.WriteStartElement( "item" );

				writer.WriteStartElement( "key" );
				keySerializer.Serialize( writer, key );
				writer.WriteEndElement();

				writer.WriteStartElement( "value" );
					TValue value = this[key];
					Type type = value.GetType();
					writer.WriteAttributeString( "type", type.AssemblyQualifiedName );

					if( !s_Serializers.TryGetValue(type,out valueSerializer) )
						valueSerializer = s_Serializers[type] = new XmlSerializer( type );
					valueSerializer.Serialize( writer, value );
				writer.WriteEndElement();

				writer.WriteEndElement();
			}
		}
		#endregion

		// ----------

		private static Dictionary<Type,XmlSerializer> s_Serializers = new Dictionary<Type,XmlSerializer>();
	}
}